import { Inject, Injectable, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import IORedis from 'ioredis';

@Injectable()
export class RedisService implements OnModuleDestroy {
  private readonly client: IORedis;

  constructor(@Inject(ConfigService) private readonly config: ConfigService) {
    const redisUrl = this.config.get<string>('REDIS_URL') || 'redis://localhost:6379';
    this.client = new IORedis(redisUrl, {
      lazyConnect: true,
      maxRetriesPerRequest: 2,
      connectTimeout: 5000,
      retryStrategy: (times) => {
        if (times > 3) return null; // stop retrying after a few attempts
        return Math.min(500 + times * 200, 1500);
      },
    });
  }

  private async withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
    return await Promise.race([
      promise,
      new Promise<T>((_, reject) =>
        setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs)
      ),
    ]);
  }

  async ping(): Promise<void> {
    if (!this.client.status || this.client.status === 'end') {
      await this.withTimeout(this.client.connect(), 5000, 'Redis connect');
    }
    const res = await this.withTimeout(this.client.ping(), 3000, 'Redis PING');
    if (res !== 'PONG') {
      throw new Error('Redis ping failed');
    }
  }

  async onModuleDestroy(): Promise<void> {
    await this.client.quit();
  }
}
